using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net;
using System.Net.Http.Headers;
using System.Text;

namespace PmSoft.Core.Http;

/// <summary>
/// 基于 HttpClient 的实现
/// </summary>
public class DefaultHttpClient : IHttpClient
{
	private readonly HttpClient _client;
	private readonly ILogger<DefaultHttpClient> _logger;
	private readonly JsonSerializerSettings _jsonSettings;

	/// <summary>
	/// 初始化 HTTP 客户端
	/// </summary>
	/// <param name="logger">日志记录器</param>
	/// <param name="client">可选的 HttpClient 实例，如果为空则创建新实例</param>
	/// <param name="jsonSettings">可选的 JSON 序列化设置</param>
	public DefaultHttpClient(ILogger<DefaultHttpClient> logger, HttpClient? client = null, JsonSerializerSettings? jsonSettings = null)
	{
		_client = client ?? new HttpClient();
		_logger = logger;
		_jsonSettings = jsonSettings ?? new JsonSerializerSettings
		{
			DateFormatString = "yyyy-MM-dd HH:mm:ss",
			NullValueHandling = NullValueHandling.Ignore
		};
	}

	/// <summary>
	/// 添加请求头
	/// </summary>
	/// <param name="name">请求头名称</param>
	/// <param name="value">请求头值</param>
	public IHttpClient WithHeader(string name, string value)
	{
		_client.DefaultRequestHeaders.Add(name, value);
		return this;
	}

	/// <summary>
	/// 批量添加请求头
	/// </summary>
	/// <param name="headers">请求头字典</param>
	public IHttpClient WithHeaders(IDictionary<string, string> headers)
	{
		foreach (var header in headers)
		{
			_client.DefaultRequestHeaders.Add(header.Key, header.Value);
		}
		return this;
	}

	/// <summary>
	/// 设置用户代理
	/// </summary>
	/// <param name="userAgent">用户代理字符串</param>
	public IHttpClient WithUserAgent(string userAgent)
	{
		_client.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
		return this;
	}

	/// <summary>
	/// 设置请求超时时间
	/// </summary>
	/// <param name="timeout">超时时间（秒）</param>
	public IHttpClient WithTimeout(int timeout)
	{
		_client.Timeout = TimeSpan.FromSeconds(timeout);
		return this;
	}

	/// <summary>
	/// 设置 Bearer 认证令牌
	/// </summary>
	/// <param name="token">认证令牌</param>
	public IHttpClient WithBearerToken(string token)
	{
		_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
		return this;
	}
	public async Task<T> GetAsync<T>(string url, object? queryParams = null, CancellationToken cancellationToken = default)
	{
		try
		{
			var requestUrl = queryParams != null
				? $"{url}?{BuildQueryString(queryParams)}"
				: url;

			using var response = await _client.GetAsync(requestUrl, cancellationToken);
			response.EnsureSuccessStatusCode();

			var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
			return Json.Parse<T>(responseContent, _jsonSettings)
				?? throw new HttpRequestException("响应内容为空");
		}
		catch (Exception ex)
		{
			HandleException(ex, url);
			throw;
		}
	}
	public T Get<T>(string url, object? queryParams = null)
	{
		return GetAsync<T>(url, queryParams).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 发送 GET 请求并返回字符串响应
	/// </summary>
	/// <param name="url">请求地址</param>
	/// <param name="queryParams">可选的查询参数对象</param>
	public string GetString(string url, object? queryParams = null)
	{
		return GetStringAsync(url, queryParams).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 发送异步 GET 请求并返回字符串响应
	/// </summary>
	/// <param name="url">请求地址</param>
	/// <param name="queryParams">可选的查询参数对象</param>
	/// <param name="cancellationToken">取消令牌</param>
	public async Task<string> GetStringAsync(string url, object? queryParams = null, CancellationToken cancellationToken = default)
	{
		try
		{
			var requestUrl = queryParams != null
				? $"{url}?{BuildQueryString(queryParams)}"
				: url;

			using var response = await _client.GetAsync(requestUrl, cancellationToken);
			response.EnsureSuccessStatusCode();
			return await response.Content.ReadAsStringAsync(cancellationToken);
		}
		catch (Exception ex)
		{
			HandleException(ex, url);
			throw;
		}
	}

	/// <summary>
	/// 发送 POST 请求并将响应反序列化为指定类型
	/// </summary>
	/// <typeparam name="T">返回数据类型</typeparam>
	/// <param name="url">请求地址</param>
	/// <param name="data">可选的请求数据</param>
	public T Post<T>(string url, object? data = null)
	{
		return PostAsync<T>(url, data).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 发送异步 POST 请求并将响应反序列化为指定类型
	/// </summary>
	/// <typeparam name="T">返回数据类型</typeparam>
	/// <param name="url">请求地址</param>
	/// <param name="data">可选的请求数据</param>
	/// <param name="cancellationToken">取消令牌</param>
	public async Task<T> PostAsync<T>(string url, object? data = null, CancellationToken cancellationToken = default)
	{
		try
		{
			using var content = new StringContent(
				Json.Stringify(data, _jsonSettings),
				Encoding.UTF8,
				"application/json"
			);

			using var response = await _client.PostAsync(url, content, cancellationToken);
			response.EnsureSuccessStatusCode();

			var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
			return Json.Parse<T>(responseContent, _jsonSettings)
				?? throw new HttpRequestException("响应内容为空");
		}
		catch (Exception ex)
		{
			HandleException(ex, url);
			throw;
		}
	}

	/// <summary>
	/// 发送 POST 请求并返回字符串响应
	/// </summary>
	/// <param name="url">请求地址</param>
	/// <param name="data">可选的请求数据</param>
	public string PostString(string url, object? data = null)
	{
		return PostStringAsync(url, data).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 发送异步 POST 请求并返回字符串响应
	/// </summary>
	/// <param name="url">请求地址</param>
	/// <param name="data">可选的请求数据</param>
	/// <param name="cancellationToken">取消令牌</param>
	public async Task<string> PostStringAsync(string url, object? data = null, CancellationToken cancellationToken = default)
	{
		try
		{
			using var content = new StringContent(
				Json.Stringify(data, _jsonSettings),
				Encoding.UTF8,
				"application/json"
			);

			using var response = await _client.PostAsync(url, content, cancellationToken);
			response.EnsureSuccessStatusCode();
			return await response.Content.ReadAsStringAsync(cancellationToken);
		}
		catch (Exception ex)
		{
			HandleException(ex, url);
			throw;
		}
	}

	/// <summary>
	/// 上传单个文件
	/// </summary>
	/// <typeparam name="T">返回数据类型</typeparam>
	/// <param name="url">上传地址</param>
	/// <param name="filePath">文件路径</param>
	/// <param name="fileParameterName">文件参数名</param>
	/// <param name="additionalData">附加表单数据</param>
	public T UploadFile<T>(string url, string filePath, string fileParameterName = "file", IDictionary<string, string>? additionalData = null)
	{
		return UploadFileAsync<T>(url, filePath, fileParameterName, additionalData).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 异步上传单个文件
	/// </summary>
	/// <typeparam name="T">返回数据类型</typeparam>
	/// <param name="url">上传地址</param>
	/// <param name="filePath">文件路径</param>
	/// <param name="fileParameterName">文件参数名</param>
	/// <param name="additionalData">附加表单数据</param>
	/// <param name="cancellationToken">取消令牌</param>
	public async Task<T> UploadFileAsync<T>(string url, string filePath, string fileParameterName = "file", IDictionary<string, string>? additionalData = null, CancellationToken cancellationToken = default)
	{
		try
		{
			using var form = new MultipartFormDataContent();
			using var fileStream = File.OpenRead(filePath);
			using var streamContent = new StreamContent(fileStream);

			streamContent.Headers.ContentType = new MediaTypeHeaderValue(GetContentType(filePath));
			form.Add(streamContent, fileParameterName, Path.GetFileName(filePath));

			if (additionalData != null)
			{
				foreach (var (key, value) in additionalData)
				{
					form.Add(new StringContent(value), key);
				}
			}

			using var response = await _client.PostAsync(url, form, cancellationToken);
			response.EnsureSuccessStatusCode();

			var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
			return Json.Parse<T>(responseContent, _jsonSettings)
				?? throw new HttpRequestException("响应内容为空");
		}
		catch (Exception ex)
		{
			HandleException(ex, url);
			throw;
		}
	}

	/// <summary>
	/// 上传多个文件
	/// </summary>
	/// <typeparam name="T">返回数据类型</typeparam>
	/// <param name="url">上传地址</param>
	/// <param name="filePaths">文件路径集合</param>
	/// <param name="fileParameterName">文件参数名</param>
	/// <param name="additionalData">附加表单数据</param>
	public T UploadFiles<T>(string url, IEnumerable<string> filePaths, string fileParameterName = "files", IDictionary<string, string>? additionalData = null)
	{
		return UploadFilesAsync<T>(url, filePaths, fileParameterName, additionalData).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 异步上传多个文件
	/// </summary>
	/// <typeparam name="T">返回数据类型</typeparam>
	/// <param name="url">上传地址</param>
	/// <param name="filePaths">文件路径集合</param>
	/// <param name="fileParameterName">文件参数名</param>
	/// <param name="additionalData">附加表单数据</param>
	/// <param name="cancellationToken">取消令牌</param>
	public async Task<T> UploadFilesAsync<T>(string url, IEnumerable<string> filePaths, string fileParameterName = "files", IDictionary<string, string>? additionalData = null, CancellationToken cancellationToken = default)
	{
		try
		{
			using var form = new MultipartFormDataContent();

			foreach (var filePath in filePaths)
			{
				using var fileStream = File.OpenRead(filePath);
				using var streamContent = new StreamContent(fileStream);
				streamContent.Headers.ContentType = new MediaTypeHeaderValue(GetContentType(filePath));
				form.Add(streamContent, fileParameterName, Path.GetFileName(filePath));
			}

			if (additionalData != null)
			{
				foreach (var (key, value) in additionalData)
				{
					form.Add(new StringContent(value), key);
				}
			}

			using var response = await _client.PostAsync(url, form, cancellationToken);
			response.EnsureSuccessStatusCode();

			var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
			return Json.Parse<T>(responseContent, _jsonSettings)
				?? throw new HttpRequestException("响应内容为空");
		}
		catch (Exception ex)
		{
			HandleException(ex, url);
			throw;
		}
	}

	/// <summary>
	/// 下载文件
	/// </summary>
	/// <param name="url">文件地址</param>
	/// <param name="saveFilePath">保存路径</param>
	public void DownloadFile(string url, string saveFilePath)
	{
		DownloadFileAsync(url, saveFilePath).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 异步下载文件
	/// </summary>
	/// <param name="url">文件地址</param>
	/// <param name="saveFilePath">保存路径</param>
	/// <param name="progress">下载进度回调</param>
	/// <param name="cancellationToken">取消令牌</param>
	public async Task DownloadFileAsync(string url, string saveFilePath, IProgress<double>? progress = null, CancellationToken cancellationToken = default)
	{
		try
		{
			using var response = await _client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
			response.EnsureSuccessStatusCode();

			var totalBytes = response.Content.Headers.ContentLength ?? -1L;
			var totalBytesRead = 0L;
			var buffer = new byte[81920];
			var isMoreToRead = true;

			using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);
			using var fileStream = new FileStream(saveFilePath, FileMode.Create, FileAccess.Write, FileShare.None, buffer.Length, true);

			do
			{
				var bytesRead = await contentStream.ReadAsync(buffer, cancellationToken);
				if (bytesRead == 0)
				{
					isMoreToRead = false;
					continue;
				}

				await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
				totalBytesRead += bytesRead;

				if (progress != null && totalBytes > 0)
				{
					var progressPercentage = (double)totalBytesRead / totalBytes * 100;
					progress.Report(progressPercentage);
				}
			}
			while (isMoreToRead);
		}
		catch (Exception ex)
		{
			HandleDownloadException(ex, url, saveFilePath);
			throw;
		}
	}

	private void HandleDownloadException(Exception ex, string url, string saveFilePath)
	{
		var errorMessage = ex switch
		{
			HttpRequestException httpEx => GetHttpErrorMessage(httpEx),
			DirectoryNotFoundException => "指定的保存目录不存在",
			IOException ioEx => $"文件IO错误: {ioEx.Message}",
			UnauthorizedAccessException => "没有权限写入文件",
			TaskCanceledException => "下载已取消",
			OperationCanceledException => "下载已取消",
			_ => $"下载失败: {ex.Message}"
		};

		_logger.LogError(ex, "下载文件失败 - URL: {Url}, 保存路径: {SavePath}, 错误: {ErrorMessage}",
			url, saveFilePath, errorMessage);
	}

	private string GetHttpErrorMessage(HttpRequestException ex)
	{
		return ex.StatusCode switch
		{
			HttpStatusCode.NotFound => "文件不存在",
			HttpStatusCode.Unauthorized => "未授权访问",
			HttpStatusCode.Forbidden => "禁止访问",
			HttpStatusCode.RequestTimeout => "请求超时",
			HttpStatusCode.BadGateway => "服务器网关错误",
			HttpStatusCode.ServiceUnavailable => "服务暂时不可用",
			HttpStatusCode.GatewayTimeout => "网关超时",
			HttpStatusCode.InternalServerError => "服务器内部错误",
			_ => $"HTTP错误: {ex.StatusCode}"
		};
	}

	private void HandleException(Exception ex, string url)
	{
		var errorMessage = ex switch
		{
			HttpRequestException httpEx => GetHttpErrorMessage(httpEx),
			JsonSerializationException jsonEx => $"JSON序列化错误: {jsonEx.Message}",
			TaskCanceledException => "请求超时或被取消",
			OperationCanceledException => "操作已取消",
			InvalidOperationException => $"无效的操作: {ex.Message}",
			ArgumentException argEx => $"参数错误: {argEx.Message}",
			_ => $"请求发生异常: {ex.Message}"
		};

		_logger.LogError(ex, "请求 {Url} 失败: {ErrorMessage}", url, errorMessage);
	}
	private string GetContentType(string filePath)
	{
		var extension = Path.GetExtension(filePath).ToLowerInvariant();
		return extension switch
		{
			".jpg" or ".jpeg" => "image/jpeg",
			".png" => "image/png",
			".gif" => "image/gif",
			".pdf" => "application/pdf",
			".doc" => "application/msword",
			".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
			".xls" => "application/vnd.ms-excel",
			".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
			".zip" => "application/zip",
			_ => "application/octet-stream"
		};
	}

	private string BuildQueryString(object queryParams)
	{
		var properties = queryParams.GetType().GetProperties();
		var queryPairs = properties
			.Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(queryParams)?.ToString() ?? "")}");
		return string.Join("&", queryPairs);
	}
}